;; Lispy

(with-eval-after-load 'lispyville
  (defun ambrevar/lispy-eval (&optional replace)
    "Like `lispy-eval' but if called with a prefix argument,
replace the expression with its result."
    (interactive "P")
    (if replace
        (lispy-eval-and-replace)
      (call-interactively #'lispy-eval)))

  (lispyville-set-key-theme
   '(operators            ; Add equivalent for lispy-delete?
     c-w                  ; Bind M-backspace to lispyville-delete-backward-word?
     (escape insert)
     slurp/barf-cp
     ;; (mark insert)
     mark-toggle                        ; TODO: Check out readme.
     ))
  (lispyville--define-key '(motion normal visual)
    (kbd "^") #'lispy-left
    (kbd "M-h") #'lispyville-previous-opening
    (kbd "M-l") #'lispyville-next-opening
    (kbd "M-j") #'lispy-down
    (kbd "M-k") #'lispy-up
    (kbd "M-H") #'lispy-up-slurp        ; lispy-down-slurp?
    (kbd "M-J") #'lispyville-drag-forward
    (kbd "M-K") #'lispyville-drag-backward
    (kbd "M-L") #'lispy-move-right      ; lispy-up-slurp?
    (kbd "C-x C-e") #'ambrevar/lispy-eval
    (kbd "C-<return>") #'lispy-split
    (kbd "S-C-<return>") #'lispy-join
    (kbd "C-1") #'lispy-describe-inline
    (kbd "C-2") #'lispy-arglist-inline
    (kbd "C-4") #'lispy-x
    (kbd "gd") #'lispy-goto-symbol
    (kbd "M-<backspace>") 'lispyville-delete-backward-word
    ;; (kbd "/") #'lispy-occur
    ;; (kbd "M-;") #'lispy-comment ; This conflicts with `iedit-toggle-selection' default binding.
    ;; TODO: lispy-eval-and-replace
    ")" #'lispy-right
    "=" #'lispyville-prettify)
  (lispyville--define-key 'insert
    (kbd "<backspace>") 'lispy-delete-backward
    (kbd "M-<backspace>") 'lispyville-delete-backward-word
    ";" 'lispy-comment
    ;; ":" 'lispy-colon ; The colon is not always used to delimit keys.
    "'" 'lispy-tick
    "`" 'lispy-backtick
    "\"" 'lispy-quotes
    "(" 'lispy-parens
    ")" 'lispy-right-nostring)
  (lispyville--define-key '(motion normal)
    ;; "q" 'lispy-ace-paren              ; REVIEW: Conflicts with magit-blame's quit.  Fixed?
    "Q" 'special-lispy-teleport         ; TODO: Go to closest parenthesis if not on one.
    "q" 'lispy-ace-paren
    ;; "f" 'lispy-ace-paren
    ;; "Q" 'lispy-ace-symbol
    ;; "t" 'lispy-ace-char
    "Y" 'lispy-new-copy
    (kbd "S-<return>") 'lispy-eval-other-window
    ;; "p" 'lispy-paste
    (kbd "M-C") 'lispy-clone            ; TODO: Go to closest parenthesis if not on one.  forward-char + lispyville-backward-up-list?
    "D" 'lispy-kill)

  (lispy-define-key lispy-mode-map-special "C" 'lispy-clone))

(defun ambrevar/init-lispy ()
  (when (require 'lispy nil t)
    (if (require 'slime nil 'noerror)
        ;; REVIEW: Fix SLIME REPL issue with "goto".
        ;; See https://github.com/abo-abo/lispy/issues/182.
        (progn
          (add-to-list 'lispy-goto-symbol-alist
                       '(slime-repl-mode lispy-goto-symbol-lisp le-lisp))
          (add-to-list 'lispy-goto-symbol-alist
                       '(slime-mrepl-mode lispy-goto-symbol-lisp le-lisp)))
      (progn
        (add-to-list 'lispy-goto-symbol-alist
                     '(sly-mrepl-mode lispy-goto-symbol-lisp le-lisp))
        (setq lispy-use-sly t)))

    (set-face-foreground 'lispy-face-hint "#FF00FF")
    (when (require 'lispyville nil t)
      (add-hook 'lispy-mode-hook 'lispyville-mode))
    (lispyville-mode)))

;; REVIEW: Remove once merged upstream:
;; https://github.com/abo-abo/lispy/issues/549
(with-eval-after-load 'lispy
  (setq lispy-eval-alist
    `((,lispy-elisp-modes lispy lispy--eval-elisp)
      ((,@lispy-clojure-modes nrepl-repl-mode cider-clojure-interaction-mode)
       le-clojure lispy-eval-clojure)
      (python-mode
       le-python lispy--eval-python lispy-eval-python-str lispy-eval-python-bnd)
      (julia-mode
       le-julia lispy-eval-julia lispy-eval-julia-str)
      (racket-mode
       le-racket lispy-eval-racket)
      (scheme-mode
       le-scheme lispy--eval-scheme)
      (lisp-mode
       le-lisp lispy--eval-lisp)
      (slime-repl-mode
       le-lisp lispy--eval-lisp)
      (slime-mrepl-mode
       le-lisp lispy--eval-lisp)
      (sly-mrepl-mode
       le-lisp lispy--eval-lisp)
      (hy-mode
       le-hy lispy--eval-hy)))

  ;; REVIEW: Remove this workaround when upstream has fixed
  ;; https://github.com/abo-abo/lispy/issues/550.
  (setq lispy-parens-preceding-syntax-alist
        '((lisp-mode . ("[#`',.@]+" "#[0-9]*" "#[.,Ss+-]" "#[0-9]+[=Aa]"))
          ;; NEW:
          (sly-mrepl-mode . ("[[:space:]]" "[#`',.@]+" "#[0-9]*" "#[.,Ss+-]" "#[0-9]+[=Aa]"))
          (emacs-lisp-mode . ("[#`',@]+" "#s" "#[0-9]+="))
          (clojure-mode . ("[`'~@]+" "#" "#\\?@?"))
          (clojurescript-mode . ("[`'~@]+" "#" "#\\?@?"))
          (clojurec-mode . ("[`'~@]+" "#" "#\\?@?"))
          (cider-repl-mode . ("[`'~@]+" "#" "#\\?@?"))
          (cider-clojure-interaction-mode . ("[`'~@]+" "#" "#\\?@?"))
          (janet-mode . ("[@;]"))
          (scheme-mode . ("[#`',@]+" "#hash"))
          (t . ("[`',@]+")))))

(provide 'init-lispy)
